/* ***** BEGIN LICENSE BLOCK *****
 * (C)opyright 2008-2009 Aplix Corporation. anselm@aplixcorp.com
 * (C)opyright 2010 tomas.mudrunka@gmail.com
 * (C)opyright 2010 exander@shy.cz
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 * ***** END LICENSE BLOCK ***** */

#define PLUGIN_NAME "dnsecValidator"
#define LOG_PREFIX PLUGIN_NAME ": "

#include <stdio.h>
#include <string.h>

#if defined(XULRUNNER_SDK)
#include <npapi.h>
//#include <npupp.h>
#include <npruntime.h>
#elif defined(ANDROID)

#undef HAVE_LONG_LONG
#include <jni.h>
#include <npapi.h>
#include <npfunctions.h>
#include <npruntime.h>
#define OSCALL
#define NPP_WRITE_TYPE (NPP_WriteProcPtr)
#define NPStringText UTF8Characters
#define NPStringLen  UTF8Length
extern JNIEnv *pluginJniEnv;

#elif defined(WEBKIT_DARWIN_SDK)

#include <Webkit/npapi.h>
#include <WebKit/npfunctions.h>
#include <WebKit/npruntime.h>
#define OSCALL

#elif defined(WEBKIT_WINMOBILE_SDK) /* WebKit SDK on Windows */

#ifndef PLATFORM
#define PLATFORM(x) defined(x)
#endif
#include <npfunctions.h>
#ifndef OSCALL
#define OSCALL WINAPI
#endif

#endif

#if defined(_WINDOWS)
#include <prtypes.h>
#include <npfunctions.h>
#endif

extern "C" {   /* use C language linkage */
  #include <ds.h>
}

static NPObject        *so       = NULL;
static NPNetscapeFuncs *npnfuncs = NULL;
static NPP              inst     = NULL;

/* NPN */

static void logmsg(const char *msg) {
#if defined(DEBUG)
#if defined(ANDROID)
	FILE *out = fopen("/sdcard/" PLUGIN_NAME ".log", "a");
	if(out) {
		fputs(msg, out);
		fclose(out);
	}
#elif !defined(_WINDOWS)
	fputs(msg, stderr);
#else
	FILE *out = fopen("\\" PLUGIN_NAME ".log", "a");
	if(out) {
		fputs(msg, out);
		fclose(out);
	}
#endif
#endif
}

static bool
hasMethod(NPObject* obj, NPIdentifier methodName) {
	logmsg(LOG_PREFIX "hasMethod\n");
	return true;
}

//validate(domain, options, nameserver, resaddrs, ttl4, ttl6)
static bool
invokeDefault(NPObject *obj, const NPVariant *args, uint32_t argCount, NPVariant *result) {
	logmsg(LOG_PREFIX "invokeDefault\n");

	//identifier
	const char *names[1] = {"value"};
	NPIdentifier ids[1];
	npnfuncs->getstringidentifiers(names, 1, ids);

	char buffer[1024];

	sprintf(buffer, "%s: %u\n", LOG_PREFIX "argCount", argCount);
	logmsg(buffer);

	for (unsigned int i=0;i<argCount;++i) {
		sprintf(buffer, "%s[%u]: %u\n", LOG_PREFIX "args", i, args[i]);
		logmsg(buffer);
	}
	
	if(argCount != 6) {
		return false;
	}

	if(!NPVARIANT_IS_STRING(args[0]) ||
	   !NPVARIANT_IS_DOUBLE(args[1]) ||
	   !NPVARIANT_IS_STRING(args[2]) ||
	   !NPVARIANT_IS_OBJECT(args[3]) ||
	   !NPVARIANT_IS_OBJECT(args[4]) ||
	   !NPVARIANT_IS_OBJECT(args[5])) {
		return false;
	}

	logmsg(LOG_PREFIX "args types ok\n");

	char *tmpptr = NULL;
	char *resaddrs = NULL;
	uint32_t ttl4 = 0;
    uint32_t ttl6 = 0;

	NPString npdomain = NPVARIANT_TO_STRING(args[0]);
	NPUTF8 domain[1024]; sprintf(domain,"%s\x00",npdomain.UTF8Characters);
	sprintf(buffer, "%s: %s\n", LOG_PREFIX "domain", domain);
	logmsg(buffer);

	uint32_t options = NPVARIANT_TO_DOUBLE(args[1]);
	sprintf(buffer, "%s: %u\n", LOG_PREFIX "options", options);
	logmsg(buffer);

	NPString npoptdnssrv = NPVARIANT_TO_STRING(args[2]);
	NPUTF8 optdnssrv[1024]; sprintf(optdnssrv,"%s\x00",npoptdnssrv.UTF8Characters);
	sprintf(buffer, "%s: %s\n", LOG_PREFIX "optdnssrv", optdnssrv);
	logmsg(buffer);

	/* call validation function from dnssec library */
	result->type = NPVariantType_Int32;
	result->value.intValue = ds_validate(domain, options, optdnssrv, &tmpptr, &ttl4, &ttl6);

	sprintf(buffer, "%s: %s\n", LOG_PREFIX "tmpptr", tmpptr);
	logmsg(buffer);
	sprintf(buffer, "%s: %u\n", LOG_PREFIX "tmpptr len", strlen(tmpptr));
	logmsg(buffer);

	/* copy result */
	resaddrs = (char *)npnfuncs->memalloc((uint32_t)strlen(tmpptr) + 1);
	sprintf(resaddrs,"%s\x00",tmpptr);

	/* free result */
	ds_free_resaddrsbuf();

	sprintf(buffer, "%s: %u\n", LOG_PREFIX "result", result->value.intValue);
	logmsg(buffer);
	sprintf(buffer, "%s: %s\n", LOG_PREFIX "resaddrs", resaddrs);
	logmsg(buffer);
	sprintf(buffer, "%s: %u\n", LOG_PREFIX "ttl4", ttl4);
	logmsg(buffer);
	sprintf(buffer, "%s: %u\n", LOG_PREFIX "ttl6", ttl6);
	logmsg(buffer);

	NPVariant npresaddrs;
	NPVariant npttl4;
	NPVariant npttl6;

	STRINGZ_TO_NPVARIANT(resaddrs, npresaddrs);
	INT32_TO_NPVARIANT(ttl4, npttl4);
	INT32_TO_NPVARIANT(ttl6, npttl6);

	npnfuncs->setproperty(inst,args[3].value.objectValue, ids[0], &npresaddrs);
	npnfuncs->setproperty(inst,args[4].value.objectValue, ids[0], &npttl4);
	npnfuncs->setproperty(inst,args[5].value.objectValue, ids[0], &npttl6);

	return true;
}

static bool
invoke(NPObject* obj, NPIdentifier methodName, const NPVariant *args, uint32_t argCount, NPVariant *result) {
	logmsg(LOG_PREFIX "invoke\n");
	int error = 1;
	char *name = npnfuncs->utf8fromidentifier(methodName);
	if(name) {
		if(!strcmp(name, "validate")) {
			logmsg(LOG_PREFIX "invoke validate\n");
			return invokeDefault(obj, args, argCount, result);
		}
	}
	// aim exception handling
	npnfuncs->setexception(obj, "exception during invocation");
	return false;
}

static bool
hasProperty(NPObject *obj, NPIdentifier propertyName) {
	logmsg(LOG_PREFIX "hasProperty\n");
	return false;
}

static bool
getProperty(NPObject *obj, NPIdentifier propertyName, NPVariant *result) {
	logmsg(LOG_PREFIX "getProperty\n");
	return false;
}

static NPClass npcRefObject = {
	NP_CLASS_STRUCT_VERSION,
	NULL,
	NULL,
	NULL,
	hasMethod,
	invoke,
	invokeDefault,
	hasProperty,
	getProperty,
	NULL,
	NULL,
};

/* NPP */

static NPError
nevv(NPMIMEType pluginType, NPP instance, uint16_t mode, int16_t argc, char *argn[], char *argv[], NPSavedData *saved) {
	inst = instance;
	logmsg(LOG_PREFIX "new\n");
	return NPERR_NO_ERROR;
}

static NPError
destroy(NPP instance, NPSavedData **save) {
	if(so)
		npnfuncs->releaseobject(so);
	so = NULL;
	logmsg(LOG_PREFIX "destroy\n");
	return NPERR_NO_ERROR;
}

static NPError
getValue(NPP instance, NPPVariable variable, void *value) {
	inst = instance;
	switch(variable) {
	default:
		logmsg(LOG_PREFIX "getvalue - default\n");
		return NPERR_GENERIC_ERROR;
	case NPPVpluginNameString:
		logmsg(LOG_PREFIX "getvalue - name string\n");
		*((char **)value) = "DNSSEC Validator plugin";
		break;
	case NPPVpluginDescriptionString:
		logmsg(LOG_PREFIX "getvalue - description string\n");
		*((char **)value) = "DNSSEC Validator plugin.";
		break;
	case NPPVpluginScriptableNPObject:
		logmsg(LOG_PREFIX "getvalue - scriptable object\n");
		if(!so)
			so = npnfuncs->createobject(instance, &npcRefObject);
		npnfuncs->retainobject(so);
		*(NPObject **)value = so;
		break;
#if defined(XULRUNNER_SDK)
	case NPPVpluginNeedsXEmbed:
		logmsg(LOG_PREFIX "getvalue - xembed\n");
		*((PRBool *)value) = PR_TRUE; //Chromium workaround set to PR_FALSE when http://code.google.com/p/chromium/issues/detail?id=38229 will get fixed
		break;
#endif
	}
	return NPERR_NO_ERROR;
}

static NPError /* expected by Safari on Darwin */
handleEvent(NPP instance, void *ev) {
	inst = instance;
	logmsg(LOG_PREFIX "handleEvent\n");
	return NPERR_NO_ERROR;
}

static NPError /* expected by Opera */
setWindow(NPP instance, NPWindow* pNPWindow) {
	inst = instance;
	logmsg(LOG_PREFIX "setWindow\n");
	return NPERR_NO_ERROR;
}

/* EXPORT */
#ifdef __cplusplus
extern "C" {
#endif

NPError OSCALL
NP_GetEntryPoints(NPPluginFuncs *nppfuncs) {
	logmsg(LOG_PREFIX "NP_GetEntryPoints\n");
	nppfuncs->version       = (NP_VERSION_MAJOR << 8) | NP_VERSION_MINOR;
	nppfuncs->newp          = nevv;
	nppfuncs->destroy       = destroy;
	nppfuncs->getvalue      = getValue;
	nppfuncs->event         = handleEvent;
	nppfuncs->setwindow     = setWindow;

	return NPERR_NO_ERROR;
}

#ifndef HIBYTE
#define HIBYTE(x) ((((uint32)(x)) & 0xff00) >> 8)
#endif

NPError OSCALL
NP_Initialize(NPNetscapeFuncs *npnf
#if defined(ANDROID)
			, NPPluginFuncs *nppfuncs, JNIEnv *env, jobject plugin_object
#elif !defined(_WINDOWS) && !defined(WEBKIT_DARWIN_SDK)
			, NPPluginFuncs *nppfuncs
#endif
			)
{
	logmsg(LOG_PREFIX "NP_Initialize\n");
	if(npnf == NULL)
		return NPERR_INVALID_FUNCTABLE_ERROR;

	if(HIBYTE(npnf->version) > NP_VERSION_MAJOR)
		return NPERR_INCOMPATIBLE_VERSION_ERROR;

	npnfuncs = npnf;
#if !defined(_WINDOWS) && !defined(WEBKIT_DARWIN_SDK)
	NP_GetEntryPoints(nppfuncs);
#endif
	return NPERR_NO_ERROR;
}

NPError
OSCALL NP_Shutdown() {
	logmsg(LOG_PREFIX "NP_Shutdown\n");
	return NPERR_NO_ERROR;
}

char *
NP_GetMIMEDescription(void) {
	logmsg(LOG_PREFIX "NP_GetMIMEDescription\n");
	return "application/x-dnssecvalidator::exander@shy.cz";
}

NPError OSCALL /* needs to be present for WebKit based browsers */
NP_GetValue(void *npp, NPPVariable variable, void *value) {
	inst = (NPP)npp;
	return getValue((NPP)npp, variable, value);
}

#ifdef __cplusplus
}
#endif
